package run.jingjing.websocket.template;

import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.context.annotation.Conditional;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.web.client.RestTemplate;
import run.jingjing.websocket.endpoint.AbstractWebSocketEndpoint;
import run.jingjing.websocket.model.RunningTopic;
import run.jingjing.websocket.utils.WebToolUtils;

import javax.annotation.Nullable;
import java.util.List;

/**
 * @author: jingjing
 * @date: 2021/6/24
 * @description:
 */
@Slf4j
@Data
public class WebSocketTemplate {

    public static final String WEBSOCKET_TOPIC_COUNT_PREFIX = "WEBSOCKET_TOPIC_COUNT_PREFIX:";

    private RestTemplate restTemplate;

    private RedisMessageListenerContainer redisMessageListenerContainer;

    private NacosServiceDiscovery nacosServiceDiscovery;

    private RedisTemplate redisTemplate;

    @Value("${spring.application.name}")
    private String serviceId;

    @Value("${server.port}")
    private String port;

    public WebSocketTemplate(RestTemplate restTemplate, RedisMessageListenerContainer redisMessageListenerContainer, NacosServiceDiscovery nacosServiceDiscovery, RedisTemplate redisTemplate) {
        this.restTemplate = restTemplate;
        this.nacosServiceDiscovery = nacosServiceDiscovery;
        this.redisTemplate = redisTemplate;
        this.redisMessageListenerContainer = redisMessageListenerContainer;
        this.redisMessageListenerContainer.setConnectionFactory(redisTemplate.getConnectionFactory());
        this.redisMessageListenerContainer.setTopicSerializer(redisTemplate.getKeySerializer());
        this.redisMessageListenerContainer.start();
    }

    /**
     * 根据用户id，向clazz对应的端点的session发送消息
     *
     * @param runningTopic
     * @param text
     * @param toUserId
     * @param fromUserId
     */
    public void publish(RunningTopic runningTopic, AbstractWebSocketEndpoint.PublishEvent event, String text, Integer toUserId, @Nullable Integer fromUserId) {
        try {
            AbstractWebSocketEndpoint.EventMessage eventMessage = new AbstractWebSocketEndpoint.EventMessage();
            eventMessage.setToUserId(toUserId);
            eventMessage.setFromUserId(fromUserId == null ? 0 : fromUserId);
            eventMessage.setEvent(event);
            eventMessage.setGroup(runningTopic.getlWsGroup().getName());
            eventMessage.setTopic(runningTopic.getlWsTopic().getName());
            eventMessage.setContent(text);
            redisTemplate.convertAndSend(runningTopic.channelTopic().toString(), eventMessage);
        } catch (Exception e) {
            log.error("WebSocketContext pulish is error", e);
            throw new RuntimeException("WebSocketContext pulish is error");
        }
    }

    /**
     * count global topic online count by clazz and topic
     */
    public Integer countOnline(String group, String topic) {
        List<ServiceInstance> instances = null;
        int count = 0;
        try {
            instances = nacosServiceDiscovery.getInstances(serviceId);
        } catch (NacosException e) {
            log.error("get service:{} ips by nacos occurred error", e);
            throw new RuntimeException("get service ips by nacos occurred error");
        }
        for (ServiceInstance instance : instances) {
            String address = instance.getHost().concat(":").concat(String.valueOf(instance.getPort()));
            Object obj = redisTemplate.opsForValue().get(getOnlineCountKey(group, address, topic));
            if (obj != null) {
                count += (Integer) obj;
            }
        }
        return count;
    }

    /**
     * get local count of redis key by clazz
     *
     * @param groupName
     * @return
     */
    public String getLocalOnlineCountKey(String groupName, String topic) {
        try {
            return groupName.concat(":")
                    .concat(topic)
                    .concat(":")
                    .concat("count")
                    .concat(":")
                    .concat(WebToolUtils.getLocalIP())
                    .concat(":")
                    .concat(port);
        } catch (Exception e) {
            log.error("get local ip occurred error", e);
            throw new RuntimeException("get local ip occurred error");
        }
    }

    /**
     * get count of redis key by clazz and address
     *
     * @param group
     * @param address
     * @param topic
     * @return
     */
    public String getOnlineCountKey(String group, String address, String topic) {
        return group
                .concat(":")
                .concat(topic)
                .concat(":")
                .concat("count")
                .concat(":")
                .concat(address);
    }

    /**
     * get local address
     *
     * @return
     */
    public String getAddress() {
        try {
            return WebToolUtils.getLocalIP().concat(":").concat(port);
        } catch (Exception e) {
            log.error("get local ip occurred error", e);
            throw new RuntimeException("get local ip occurred error");
        }
    }
}
